/**
 * at.js - 1.5.4
 * Copyright (c) 2018 chord.luo <chord.luo@gmail.com>;
 * Homepage: http://ichord.github.com/At.js
 * License: MIT
 */
;(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module unless amdModuleId is set
        define(['jquery'], function (a0) {
            return factory(a0)
        })
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory(require('jquery'))
    } else {
        factory(jQuery)
    }
})(this, function ($) {
    var DEFAULT_CALLBACKS, KEY_CODE

    KEY_CODE = {
        ESC: 27,
        TAB: 9,
        ENTER: 13,
        CTRL: 17,
        A: 65,
        P: 80,
        N: 78,
        LEFT: 37,
        UP: 38,
        RIGHT: 39,
        DOWN: 40,
        BACKSPACE: 8,
        SPACE: 32
    }

    DEFAULT_CALLBACKS = {
        beforeSave: function (data) {
            return Controller.arrayToDefaultHash(data)
        },
        matcher: function (flag, subtext, should_startWithSpace, acceptSpaceBar) {
            var _a, _y, match, regexp, space
            flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
            if (should_startWithSpace) {
                flag = '(?:^|\\s)' + flag
            }
            _a = decodeURI('%C3%80')
            _y = decodeURI('%C3%BF')
            space = acceptSpaceBar ? ' ' : ''
            regexp = new RegExp(
                flag +
                    '([A-Za-z' +
                    _a +
                    '-' +
                    _y +
                    '0-9_' +
                    space +
                    "'.+-]*)$|" +
                    flag +
                    '([^\\x00-\\xff]*)$',
                'gi'
            )
            match = regexp.exec(subtext)
            if (match) {
                return match[2] || match[1]
            } else {
                return null
            }
        },
        filter: function (query, data, searchKey) {
            var _results, i, item, len
            _results = []
            for (i = 0, len = data.length; i < len; i++) {
                item = data[i]
                if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
                    _results.push(item)
                }
            }
            return _results
        },
        remoteFilter: null,
        sorter: function (query, items, searchKey) {
            var _results, i, item, len
            if (!query) {
                return items
            }
            _results = []
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i]
                item.atwho_order = new String(item[searchKey])
                    .toLowerCase()
                    .indexOf(query.toLowerCase())
                if (item.atwho_order > -1) {
                    _results.push(item)
                }
            }
            return _results.sort(function (a, b) {
                return a.atwho_order - b.atwho_order
            })
        },
        tplEval: function (tpl, map) {
            var error, error1, template
            template = tpl
            try {
                if (typeof tpl !== 'string') {
                    template = tpl(map)
                }
                return template.replace(/\$\{([^\}]*)\}/g, function (tag, key, pos) {
                    return map[key]
                })
            } catch (error1) {
                error = error1
                return ''
            }
        },
        highlighter: function (li, query) {
            var regexp
            if (!query) {
                return li
            }
            regexp = new RegExp(
                '>\\s*([^<]*?)(' + query.replace('+', '\\+') + ')([^<]*)\\s*<',
                'ig'
            )
            return li.replace(regexp, function (str, $1, $2, $3) {
                return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <'
            })
        },
        beforeInsert: function (value, $li, e) {
            return value
        },
        beforeReposition: function (offset) {
            return offset
        },
        afterMatchFailed: function (at, el) {}
    }

    var App

    App = (function () {
        function App(inputor) {
            this.currentFlag = null
            this.controllers = {}
            this.aliasMaps = {}
            this.$inputor = $(inputor)
            this.setupRootElement()
            this.listen()
        }

        App.prototype.createContainer = function (doc) {
            var ref
            if ((ref = this.$el) != null) {
                ref.remove()
            }
            return $(doc.body).append((this.$el = $("<div class='atwho-container'></div>")))
        }

        App.prototype.setupRootElement = function (iframe, asRoot) {
            var error, error1
            if (asRoot == null) {
                asRoot = false
            }
            if (iframe) {
                this.window = iframe.contentWindow
                this.document = iframe.contentDocument || this.window.document
                this.iframe = iframe
            } else {
                this.document = this.$inputor[0].ownerDocument
                this.window = this.document.defaultView || this.document.parentWindow
                try {
                    this.iframe = this.window.frameElement
                } catch (error1) {
                    error = error1
                    this.iframe = null
                    if ($.fn.atwho.debug) {
                        throw new Error(
                            'iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n' +
                                error
                        )
                    }
                }
            }
            return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document)
        }

        App.prototype.controller = function (at) {
            var c, current, currentFlag, ref
            if (this.aliasMaps[at]) {
                current = this.controllers[this.aliasMaps[at]]
            } else {
                ref = this.controllers
                for (currentFlag in ref) {
                    c = ref[currentFlag]
                    if (currentFlag === at) {
                        current = c
                        break
                    }
                }
            }
            if (current) {
                return current
            } else {
                return this.controllers[this.currentFlag]
            }
        }

        App.prototype.setContextFor = function (at) {
            this.currentFlag = at
            return this
        }

        App.prototype.reg = function (flag, setting) {
            var base, controller
            controller =
                (base = this.controllers)[flag] ||
                (base[flag] = this.$inputor.is('[contentEditable]')
                    ? new EditableController(this, flag)
                    : new TextareaController(this, flag))
            if (setting.alias) {
                this.aliasMaps[setting.alias] = flag
            }
            controller.init(setting)
            return this
        }

        App.prototype.listen = function () {
            return this.$inputor
                .on(
                    'compositionstart',
                    (function (_this) {
                        return function (e) {
                            var ref
                            if ((ref = _this.controller()) != null) {
                                ref.view.hide()
                            }
                            _this.isComposing = true
                            return null
                        }
                    })(this)
                )
                .on(
                    'compositionend',
                    (function (_this) {
                        return function (e) {
                            _this.isComposing = false
                            setTimeout(function (e) {
                                return _this.dispatch(e)
                            })
                            return null
                        }
                    })(this)
                )
                .on(
                    'keyup.atwhoInner',
                    (function (_this) {
                        return function (e) {
                            return _this.onKeyup(e)
                        }
                    })(this)
                )
                .on(
                    'keydown.atwhoInner',
                    (function (_this) {
                        return function (e) {
                            return _this.onKeydown(e)
                        }
                    })(this)
                )
                .on(
                    'blur.atwhoInner',
                    (function (_this) {
                        return function (e) {
                            var c
                            if ((c = _this.controller())) {
                                c.expectedQueryCBId = null
                                return c.view.hide(e, c.getOpt('displayTimeout'))
                            }
                        }
                    })(this)
                )
                .on(
                    'click.atwhoInner',
                    (function (_this) {
                        return function (e) {
                            return _this.dispatch(e)
                        }
                    })(this)
                )
                .on(
                    'scroll.atwhoInner',
                    (function (_this) {
                        return function () {
                            var lastScrollTop
                            lastScrollTop = _this.$inputor.scrollTop()
                            return function (e) {
                                var currentScrollTop, ref
                                currentScrollTop = e.target.scrollTop
                                if (lastScrollTop !== currentScrollTop) {
                                    if ((ref = _this.controller()) != null) {
                                        ref.view.hide(e)
                                    }
                                }
                                lastScrollTop = currentScrollTop
                                return true
                            }
                        }
                    })(this)()
                )
        }

        App.prototype.shutdown = function () {
            var _, c, ref
            ref = this.controllers
            for (_ in ref) {
                c = ref[_]
                c.destroy()
                delete this.controllers[_]
            }
            this.$inputor.off('.atwhoInner')
            return this.$el.remove()
        }

        App.prototype.dispatch = function (e) {
            var _, c, ref, results
            if (void 0 === e) {
                return
            }
            ref = this.controllers
            results = []
            for (_ in ref) {
                c = ref[_]
                results.push(c.lookUp(e))
            }
            return results
        }

        App.prototype.onKeyup = function (e) {
            var ref
            switch (e.keyCode) {
                case KEY_CODE.ESC:
                    e.preventDefault()
                    if ((ref = this.controller()) != null) {
                        ref.view.hide()
                    }
                    break
                case KEY_CODE.DOWN:
                case KEY_CODE.UP:
                case KEY_CODE.CTRL:
                case KEY_CODE.ENTER:
                    $.noop()
                    break
                case KEY_CODE.P:
                case KEY_CODE.N:
                    if (!e.ctrlKey) {
                        this.dispatch(e)
                    }
                    break
                default:
                    this.dispatch(e)
            }
        }

        App.prototype.onKeydown = function (e) {
            var ref, view
            view = (ref = this.controller()) != null ? ref.view : void 0
            if (!(view && view.visible())) {
                return
            }
            switch (e.keyCode) {
                case KEY_CODE.ESC:
                    e.preventDefault()
                    view.hide(e)
                    break
                case KEY_CODE.UP:
                    e.preventDefault()
                    view.prev()
                    break
                case KEY_CODE.DOWN:
                    e.preventDefault()
                    view.next()
                    break
                case KEY_CODE.P:
                    if (!e.ctrlKey) {
                        return
                    }
                    e.preventDefault()
                    view.prev()
                    break
                case KEY_CODE.N:
                    if (!e.ctrlKey) {
                        return
                    }
                    e.preventDefault()
                    view.next()
                    break
                case KEY_CODE.TAB:
                case KEY_CODE.ENTER:
                case KEY_CODE.SPACE:
                    if (!view.visible()) {
                        return
                    }
                    if (
                        !this.controller().getOpt('spaceSelectsMatch') &&
                        e.keyCode === KEY_CODE.SPACE
                    ) {
                        return
                    }
                    if (
                        !this.controller().getOpt('tabSelectsMatch') &&
                        e.keyCode === KEY_CODE.TAB
                    ) {
                        return
                    }
                    if (view.highlighted()) {
                        e.preventDefault()
                        view.choose(e)
                    } else {
                        view.hide(e)
                    }
                    break
                default:
                    $.noop()
            }
        }

        return App
    })()

    var Controller,
        slice = [].slice

    Controller = (function () {
        Controller.prototype.uid = function () {
            return (Math.random().toString(16) + '000000000').substr(2, 8) + new Date().getTime()
        }

        function Controller(app, at1) {
            this.app = app
            this.at = at1
            this.$inputor = this.app.$inputor
            this.id = this.$inputor[0].id || this.uid()
            this.expectedQueryCBId = null
            this.setting = null
            this.query = null
            this.pos = 0
            this.range = null
            if ((this.$el = $('#atwho-ground-' + this.id, this.app.$el)).length === 0) {
                this.app.$el.append((this.$el = $("<div id='atwho-ground-" + this.id + "'></div>")))
            }
            this.model = new Model(this)
            this.view = new View(this)
        }

        Controller.prototype.init = function (setting) {
            this.setting = $.extend({}, this.setting || $.fn.atwho['default'], setting)
            this.view.init()
            return this.model.reload(this.setting.data)
        }

        Controller.prototype.destroy = function () {
            this.trigger('beforeDestroy')
            this.model.destroy()
            this.view.destroy()
            return this.$el.remove()
        }

        Controller.prototype.callDefault = function () {
            var args, error, error1, funcName
            ;(funcName = arguments[0]),
                (args = 2 <= arguments.length ? slice.call(arguments, 1) : [])
            try {
                return DEFAULT_CALLBACKS[funcName].apply(this, args)
            } catch (error1) {
                error = error1
                return $.error(error + " Or maybe At.js doesn't have function " + funcName)
            }
        }

        Controller.prototype.trigger = function (name, data) {
            var alias, eventName
            if (data == null) {
                data = []
            }
            data.push(this)
            alias = this.getOpt('alias')
            eventName = alias ? name + '-' + alias + '.atwho' : name + '.atwho'
            return this.$inputor.trigger(eventName, data)
        }

        Controller.prototype.callbacks = function (funcName) {
            return this.getOpt('callbacks')[funcName] || DEFAULT_CALLBACKS[funcName]
        }

        Controller.prototype.getOpt = function (at, default_value) {
            var e, error1
            try {
                return this.setting[at]
            } catch (error1) {
                e = error1
                return null
            }
        }

        Controller.prototype.insertContentFor = function ($li) {
            var data, tpl
            tpl = this.getOpt('insertTpl')
            data = $.extend({}, $li.data('item-data'), {
                'atwho-at': this.at
            })
            return this.callbacks('tplEval').call(this, tpl, data, 'onInsert')
        }

        Controller.prototype.renderView = function (data) {
            var searchKey
            searchKey = this.getOpt('searchKey')
            data = this.callbacks('sorter').call(
                this,
                this.query.text,
                data.slice(0, 1001),
                searchKey
            )
            return this.view.render(data.slice(0, this.getOpt('limit')))
        }

        Controller.arrayToDefaultHash = function (data) {
            var i, item, len, results
            if (!$.isArray(data)) {
                return data
            }
            results = []
            for (i = 0, len = data.length; i < len; i++) {
                item = data[i]
                if ($.isPlainObject(item)) {
                    results.push(item)
                } else {
                    results.push({
                        name: item
                    })
                }
            }
            return results
        }

        Controller.prototype.lookUp = function (e) {
            var query, wait
            if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
                return
            }
            if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
                return
            }
            query = this.catchQuery(e)
            if (!query) {
                this.expectedQueryCBId = null
                return query
            }
            this.app.setContextFor(this.at)
            if ((wait = this.getOpt('delay'))) {
                this._delayLookUp(query, wait)
            } else {
                this._lookUp(query)
            }
            return query
        }

        Controller.prototype._delayLookUp = function (query, wait) {
            var now, remaining
            now = Date.now ? Date.now() : new Date().getTime()
            this.previousCallTime || (this.previousCallTime = now)
            remaining = wait - (now - this.previousCallTime)
            if (0 < remaining && remaining < wait) {
                this.previousCallTime = now
                this._stopDelayedCall()
                return (this.delayedCallTimeout = setTimeout(
                    (function (_this) {
                        return function () {
                            _this.previousCallTime = 0
                            _this.delayedCallTimeout = null
                            return _this._lookUp(query)
                        }
                    })(this),
                    wait
                ))
            } else {
                this._stopDelayedCall()
                if (this.previousCallTime !== now) {
                    this.previousCallTime = 0
                }
                return this._lookUp(query)
            }
        }

        Controller.prototype._stopDelayedCall = function () {
            if (this.delayedCallTimeout) {
                clearTimeout(this.delayedCallTimeout)
                return (this.delayedCallTimeout = null)
            }
        }

        Controller.prototype._generateQueryCBId = function () {
            return {}
        }

        Controller.prototype._lookUp = function (query) {
            var _callback
            _callback = function (queryCBId, data) {
                if (queryCBId !== this.expectedQueryCBId) {
                    return
                }
                if (data && data.length > 0) {
                    return this.renderView(this.constructor.arrayToDefaultHash(data))
                } else {
                    return this.view.hide()
                }
            }
            this.expectedQueryCBId = this._generateQueryCBId()
            return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId))
        }

        return Controller
    })()

    var TextareaController,
        extend = function (child, parent) {
            for (var key in parent) {
                if (hasProp.call(parent, key)) child[key] = parent[key]
            }
            function ctor() {
                this.constructor = child
            }
            ctor.prototype = parent.prototype
            child.prototype = new ctor()
            child.__super__ = parent.prototype
            return child
        },
        hasProp = {}.hasOwnProperty

    TextareaController = (function (superClass) {
        extend(TextareaController, superClass)

        function TextareaController() {
            return TextareaController.__super__.constructor.apply(this, arguments)
        }

        TextareaController.prototype.catchQuery = function () {
            var caretPos, content, end, isString, query, start, subtext
            content = this.$inputor.val()
            caretPos = this.$inputor.caret('pos', {
                iframe: this.app.iframe
            })
            subtext = content.slice(0, caretPos)
            query = this.callbacks('matcher').call(
                this,
                this.at,
                subtext,
                this.getOpt('startWithSpace'),
                this.getOpt('acceptSpaceBar')
            )
            isString = typeof query === 'string'
            if (isString && query.length < this.getOpt('minLen', 0)) {
                return
            }
            if (isString && query.length <= this.getOpt('maxLen', 20)) {
                start = caretPos - query.length
                end = start + query.length
                this.pos = start
                query = {
                    text: query,
                    headPos: start,
                    endPos: end
                }
                this.trigger('matched', [this.at, query.text])
            } else {
                query = null
                this.view.hide()
            }
            return (this.query = query)
        }

        TextareaController.prototype.rect = function () {
            var c, iframeOffset, scaleBottom
            if (
                !(c = this.$inputor.caret('offset', this.pos - 1, {
                    iframe: this.app.iframe
                }))
            ) {
                return
            }
            if (this.app.iframe && !this.app.iframeAsRoot) {
                iframeOffset = $(this.app.iframe).offset()
                c.left += iframeOffset.left
                c.top += iframeOffset.top
            }
            scaleBottom = this.app.document.selection ? 0 : 2
            return {
                left: c.left,
                top: c.top,
                bottom: c.top + c.height + scaleBottom
            }
        }

        TextareaController.prototype.insert = function (content, $li) {
            var $inputor, source, startStr, suffix, text
            $inputor = this.$inputor
            source = $inputor.val()
            startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0))
            suffix = (suffix = this.getOpt('suffix')) === '' ? suffix : suffix || ' '
            content += suffix
            text = '' + startStr + content + source.slice(this.query['endPos'] || 0)
            $inputor.val(text)
            $inputor.caret('pos', startStr.length + content.length, {
                iframe: this.app.iframe
            })
            if (!$inputor.is(':focus')) {
                $inputor.focus()
            }
            return $inputor.change()
        }

        return TextareaController
    })(Controller)

    var EditableController,
        extend = function (child, parent) {
            for (var key in parent) {
                if (hasProp.call(parent, key)) child[key] = parent[key]
            }
            function ctor() {
                this.constructor = child
            }
            ctor.prototype = parent.prototype
            child.prototype = new ctor()
            child.__super__ = parent.prototype
            return child
        },
        hasProp = {}.hasOwnProperty

    EditableController = (function (superClass) {
        extend(EditableController, superClass)

        function EditableController() {
            return EditableController.__super__.constructor.apply(this, arguments)
        }

        EditableController.prototype._getRange = function () {
            var sel
            sel = this.app.window.getSelection()
            if (sel.rangeCount > 0) {
                return sel.getRangeAt(0)
            }
        }

        EditableController.prototype._setRange = function (position, node, range) {
            if (range == null) {
                range = this._getRange()
            }
            if (!(range && node)) {
                return
            }
            node = $(node)[0]
            if (position === 'after') {
                range.setEndAfter(node)
                range.setStartAfter(node)
            } else {
                range.setEndBefore(node)
                range.setStartBefore(node)
            }
            range.collapse(false)
            return this._clearRange(range)
        }

        EditableController.prototype._clearRange = function (range) {
            var sel
            if (range == null) {
                range = this._getRange()
            }
            sel = this.app.window.getSelection()
            if (this.ctrl_a_pressed == null) {
                sel.removeAllRanges()
                return sel.addRange(range)
            }
        }

        EditableController.prototype._movingEvent = function (e) {
            var ref
            return (
                e.type === 'click' ||
                (ref = e.which) === KEY_CODE.RIGHT ||
                ref === KEY_CODE.LEFT ||
                ref === KEY_CODE.UP ||
                ref === KEY_CODE.DOWN
            )
        }

        EditableController.prototype._unwrap = function (node) {
            var next
            node = $(node).unwrap().get(0)
            if ((next = node.nextSibling) && next.nodeValue) {
                node.nodeValue += next.nodeValue
                $(next).remove()
            }
            return node
        }

        EditableController.prototype.catchQuery = function (e) {
            var $inserted,
                $query,
                _range,
                index,
                inserted,
                isString,
                lastNode,
                matched,
                offset,
                query,
                query_content,
                range
            if (!(range = this._getRange())) {
                return
            }
            if (!range.collapsed) {
                return
            }
            if (e.which === KEY_CODE.ENTER) {
                ;($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap()
                if ($query.is(':empty')) {
                    $query.remove()
                }
                ;($query = $('.atwho-query', this.app.document))
                    .text($query.text())
                    .contents()
                    .last()
                    .unwrap()
                this._clearRange()
                return
            }
            if (/firefox/i.test(navigator.userAgent)) {
                if ($(range.startContainer).is(this.$inputor)) {
                    this._clearRange()
                    return
                }
                if (
                    e.which === KEY_CODE.BACKSPACE &&
                    range.startContainer.nodeType === document.ELEMENT_NODE &&
                    (offset = range.startOffset - 1) >= 0
                ) {
                    _range = range.cloneRange()
                    _range.setStart(range.startContainer, offset)
                    if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
                        inserted = $(range.startContainer).contents().get(offset)
                        this._setRange('after', $(inserted).contents().last())
                    }
                } else if (
                    e.which === KEY_CODE.LEFT &&
                    range.startContainer.nodeType === document.TEXT_NODE
                ) {
                    $inserted = $(range.startContainer.previousSibling)
                    if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
                        this._setRange('after', $inserted.contents().last())
                    }
                }
            }
            $(range.startContainer)
                .closest('.atwho-inserted')
                .addClass('atwho-query')
                .siblings()
                .removeClass('atwho-query')
            if (
                ($query = $('.atwho-query', this.app.document)).length > 0 &&
                $query.is(':empty') &&
                $query.text().length === 0
            ) {
                $query.remove()
            }
            if (!this._movingEvent(e)) {
                $query.removeClass('atwho-inserted')
            }
            if ($query.length > 0) {
                switch (e.which) {
                    case KEY_CODE.LEFT:
                        this._setRange('before', $query.get(0), range)
                        $query.removeClass('atwho-query')
                        return
                    case KEY_CODE.RIGHT:
                        this._setRange('after', $query.get(0).nextSibling, range)
                        $query.removeClass('atwho-query')
                        return
                }
            }
            if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
                $query.empty().html(query_content).attr('data-atwho-at-query', null)
                this._setRange('after', $query.get(0), range)
            }
            _range = range.cloneRange()
            _range.setStart(range.startContainer, 0)
            matched = this.callbacks('matcher').call(
                this,
                this.at,
                _range.toString(),
                this.getOpt('startWithSpace'),
                this.getOpt('acceptSpaceBar')
            )
            isString = typeof matched === 'string'
            if (
                $query.length === 0 &&
                isString &&
                (index = range.startOffset - this.at.length - matched.length) >= 0
            ) {
                range.setStart(range.startContainer, index)
                $query = $('<span/>', this.app.document)
                    .attr(this.getOpt('editableAtwhoQueryAttrs'))
                    .addClass('atwho-query')
                range.surroundContents($query.get(0))
                lastNode = $query.contents().last().get(0)
                if (lastNode) {
                    if (/firefox/i.test(navigator.userAgent)) {
                        range.setStart(lastNode, lastNode.length)
                        range.setEnd(lastNode, lastNode.length)
                        this._clearRange(range)
                    } else {
                        this._setRange('after', lastNode, range)
                    }
                }
            }
            if (isString && matched.length < this.getOpt('minLen', 0)) {
                return
            }
            if (isString && matched.length <= this.getOpt('maxLen', 20)) {
                query = {
                    text: matched,
                    el: $query
                }
                this.trigger('matched', [this.at, query.text])
                return (this.query = query)
            } else {
                this.view.hide()
                this.query = {
                    el: $query
                }
                if ($query.text().indexOf(this.at) >= 0) {
                    if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
                        $query.removeClass('atwho-query')
                    } else if (
                        false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)
                    ) {
                        this._setRange(
                            'after',
                            this._unwrap($query.text($query.text()).contents().first())
                        )
                    }
                }
                return null
            }
        }

        EditableController.prototype.rect = function () {
            var $iframe, iframeOffset, rect
            rect = this.query.el.offset()
            if (!(rect && this.query.el[0].getClientRects().length)) {
                return
            }
            if (this.app.iframe && !this.app.iframeAsRoot) {
                iframeOffset = ($iframe = $(this.app.iframe)).offset()
                rect.left += iframeOffset.left - this.$inputor.scrollLeft()
                rect.top += iframeOffset.top - this.$inputor.scrollTop()
            }
            rect.bottom = rect.top + this.query.el.height()
            return rect
        }

        EditableController.prototype.insert = function (content, $li) {
            var data, overrides, range, suffix, suffixNode
            if (!this.$inputor.is(':focus')) {
                this.$inputor.focus()
            }
            overrides = this.getOpt('functionOverrides')
            if (overrides.insert) {
                return overrides.insert.call(this, content, $li)
            }
            suffix = (suffix = this.getOpt('suffix')) === '' ? suffix : suffix || '\u00A0'
            data = $li.data('item-data')
            this.query.el
                .removeClass('atwho-query')
                .addClass('atwho-inserted')
                .html(content)
                .attr('data-atwho-at-query', '' + data['atwho-at'] + this.query.text)
                .attr('contenteditable', 'false')
            if ((range = this._getRange())) {
                if (this.query.el.length) {
                    range.setEndAfter(this.query.el[0])
                }
                range.collapse(false)
                range.insertNode((suffixNode = this.app.document.createTextNode('' + suffix)))
                this._setRange('after', suffixNode, range)
            }
            if (!this.$inputor.is(':focus')) {
                this.$inputor.focus()
            }
            return this.$inputor.change()
        }

        return EditableController
    })(Controller)

    var Model

    Model = (function () {
        function Model(context) {
            this.context = context
            this.at = this.context.at
            this.storage = this.context.$inputor
        }

        Model.prototype.destroy = function () {
            return this.storage.data(this.at, null)
        }

        Model.prototype.saved = function () {
            return this.fetch() > 0
        }

        Model.prototype.query = function (query, callback) {
            var _remoteFilter, data, searchKey
            data = this.fetch()
            searchKey = this.context.getOpt('searchKey')
            data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || []
            _remoteFilter = this.context.callbacks('remoteFilter')
            if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
                return callback(data)
            } else {
                return _remoteFilter.call(this.context, query, callback)
            }
        }

        Model.prototype.fetch = function () {
            return this.storage.data(this.at) || []
        }

        Model.prototype.save = function (data) {
            return this.storage.data(
                this.at,
                this.context.callbacks('beforeSave').call(this.context, data || [])
            )
        }

        Model.prototype.load = function (data) {
            if (!(this.saved() || !data)) {
                return this._load(data)
            }
        }

        Model.prototype.reload = function (data) {
            return this._load(data)
        }

        Model.prototype._load = function (data) {
            if (typeof data === 'string') {
                return $.ajax(data, {
                    dataType: 'json'
                }).done(
                    (function (_this) {
                        return function (data) {
                            return _this.save(data)
                        }
                    })(this)
                )
            } else {
                return this.save(data)
            }
        }

        return Model
    })()

    var View

    View = (function () {
        function View(context) {
            this.context = context
            this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>")
            this.$elUl = this.$el.children()
            this.timeoutID = null
            this.context.$el.append(this.$el)
            this.bindEvent()
        }

        View.prototype.init = function () {
            var header_tpl, id
            id = this.context.getOpt('alias') || this.context.at.charCodeAt(0)
            header_tpl = this.context.getOpt('headerTpl')
            if (header_tpl && this.$el.children().length === 1) {
                this.$el.prepend(header_tpl)
            }
            return this.$el.attr({
                id: 'at-view-' + id
            })
        }

        View.prototype.destroy = function () {
            return this.$el.remove()
        }

        View.prototype.bindEvent = function () {
            var $menu, lastCoordX, lastCoordY
            $menu = this.$el.find('ul')
            lastCoordX = 0
            lastCoordY = 0
            return $menu
                .on(
                    'mousemove.atwho-view',
                    'li',
                    (function (_this) {
                        return function (e) {
                            var $cur
                            if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
                                return
                            }
                            lastCoordX = e.clientX
                            lastCoordY = e.clientY
                            $cur = $(e.currentTarget)
                            if ($cur.hasClass('cur')) {
                                return
                            }
                            $menu.find('.cur').removeClass('cur')
                            return $cur.addClass('cur')
                        }
                    })(this)
                )
                .on(
                    'click.atwho-view',
                    'li',
                    (function (_this) {
                        return function (e) {
                            $menu.find('.cur').removeClass('cur')
                            $(e.currentTarget).addClass('cur')
                            _this.choose(e)
                            return e.preventDefault()
                        }
                    })(this)
                )
        }

        View.prototype.visible = function () {
            return $.expr.filters.visible(this.$el[0])
        }

        View.prototype.highlighted = function () {
            return this.$el.find('.cur').length > 0
        }

        View.prototype.choose = function (e) {
            var $li, content
            if (($li = this.$el.find('.cur')).length) {
                content = this.context.insertContentFor($li)
                this.context._stopDelayedCall()
                this.context.insert(
                    this.context.callbacks('beforeInsert').call(this.context, content, $li, e),
                    $li
                )
                this.context.trigger('inserted', [$li, e])
                this.hide(e)
            }
            if (this.context.getOpt('hideWithoutSuffix')) {
                return (this.stopShowing = true)
            }
        }

        View.prototype.reposition = function (rect) {
            var _window, offset, overflowOffset, ref
            _window = this.context.app.iframeAsRoot ? this.context.app.window : window
            if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
                rect.bottom = rect.top - this.$el.height()
            }
            if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
                rect.left = overflowOffset
            }
            offset = {
                left: rect.left,
                top: rect.bottom
            }
            if ((ref = this.context.callbacks('beforeReposition')) != null) {
                ref.call(this.context, offset)
            }
            this.$el.offset(offset)
            return this.context.trigger('reposition', [offset])
        }

        View.prototype.next = function () {
            var cur, next, nextEl, offset
            cur = this.$el.find('.cur').removeClass('cur')
            next = cur.next()
            if (!next.length) {
                next = this.$el.find('li:first')
            }
            next.addClass('cur')
            nextEl = next[0]
            offset =
                nextEl.offsetTop +
                nextEl.offsetHeight +
                (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0)
            return this.scrollTop(Math.max(0, offset - this.$el.height()))
        }

        View.prototype.prev = function () {
            var cur, offset, prev, prevEl
            cur = this.$el.find('.cur').removeClass('cur')
            prev = cur.prev()
            if (!prev.length) {
                prev = this.$el.find('li:last')
            }
            prev.addClass('cur')
            prevEl = prev[0]
            offset =
                prevEl.offsetTop +
                prevEl.offsetHeight +
                (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0)
            return this.scrollTop(Math.max(0, offset - this.$el.height()))
        }

        View.prototype.scrollTop = function (scrollTop) {
            var scrollDuration
            scrollDuration = this.context.getOpt('scrollDuration')
            if (scrollDuration) {
                return this.$elUl.animate(
                    {
                        scrollTop: scrollTop
                    },
                    scrollDuration
                )
            } else {
                return this.$elUl.scrollTop(scrollTop)
            }
        }

        View.prototype.show = function () {
            var rect
            if (this.stopShowing) {
                this.stopShowing = false
                return
            }
            if (!this.visible()) {
                this.$el.show()
                this.$el.scrollTop(0)
                this.context.trigger('shown')
            }
            if ((rect = this.context.rect())) {
                return this.reposition(rect)
            }
        }

        View.prototype.hide = function (e, time) {
            var callback
            if (!this.visible()) {
                return
            }
            if (isNaN(time)) {
                this.$el.hide()
                return this.context.trigger('hidden', [e])
            } else {
                callback = (function (_this) {
                    return function () {
                        return _this.hide()
                    }
                })(this)
                clearTimeout(this.timeoutID)
                return (this.timeoutID = setTimeout(callback, time))
            }
        }

        View.prototype.render = function (list) {
            var $li, $ul, i, item, len, li, tpl
            if (!($.isArray(list) && list.length > 0)) {
                this.hide()
                return
            }
            this.$el.find('ul').empty()
            $ul = this.$el.find('ul')
            tpl = this.context.getOpt('displayTpl')
            for (i = 0, len = list.length; i < len; i++) {
                item = list[i]
                item = $.extend({}, item, {
                    'atwho-at': this.context.at
                })
                li = this.context.callbacks('tplEval').call(this.context, tpl, item, 'onDisplay')
                $li = $(
                    this.context
                        .callbacks('highlighter')
                        .call(this.context, li, this.context.query.text)
                )
                $li.data('item-data', item)
                $ul.append($li)
            }
            this.show()
            if (this.context.getOpt('highlightFirst')) {
                return $ul.find('li:first').addClass('cur')
            }
        }

        return View
    })()

    var Api

    Api = {
        load: function (at, data) {
            var c
            if ((c = this.controller(at))) {
                return c.model.load(data)
            }
        },
        isSelecting: function () {
            var ref
            return !!((ref = this.controller()) != null ? ref.view.visible() : void 0)
        },
        hide: function () {
            var ref
            return (ref = this.controller()) != null ? ref.view.hide() : void 0
        },
        reposition: function () {
            var c
            if ((c = this.controller())) {
                return c.view.reposition(c.rect())
            }
        },
        setIframe: function (iframe, asRoot) {
            this.setupRootElement(iframe, asRoot)
            return null
        },
        run: function () {
            return this.dispatch()
        },
        destroy: function () {
            this.shutdown()
            return this.$inputor.data('atwho', null)
        }
    }

    $.fn.atwho = function (method) {
        var _args, result
        _args = arguments
        result = null
        this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(
            function () {
                var $this, app
                if (!(app = ($this = $(this)).data('atwho'))) {
                    $this.data('atwho', (app = new App(this)))
                }
                if (typeof method === 'object' || !method) {
                    return app.reg(method.at, method)
                } else if (Api[method] && app) {
                    return (result = Api[method].apply(app, Array.prototype.slice.call(_args, 1)))
                } else {
                    return $.error('Method ' + method + ' does not exist on jQuery.atwho')
                }
            }
        )
        if (result != null) {
            return result
        } else {
            return this
        }
    }

    $.fn.atwho['default'] = {
        at: void 0,
        alias: void 0,
        data: null,
        displayTpl: '<li>${name}</li>',
        insertTpl: '${atwho-at}${name}',
        headerTpl: null,
        callbacks: DEFAULT_CALLBACKS,
        functionOverrides: {},
        searchKey: 'name',
        suffix: void 0,
        hideWithoutSuffix: false,
        startWithSpace: true,
        acceptSpaceBar: false,
        highlightFirst: true,
        limit: 5,
        maxLen: 20,
        minLen: 0,
        displayTimeout: 300,
        delay: null,
        spaceSelectsMatch: false,
        tabSelectsMatch: true,
        editableAtwhoQueryAttrs: {},
        scrollDuration: 150,
        suspendOnComposing: true,
        lookUpOnClick: true
    }

    $.fn.atwho.debug = false
})
